//! 让普通值能响应式 let state = ref('abc') 当然 对象也是也已的
//! ref和reactive区别  reactive内部采用Proxy  ref内部用的是defineProperty
import {track, trigger} from "./effect";
import {TrackGet, TriggerOrSet} from "./operators";
import {hasChanged, isArray, isObject} from "./utils";
import {reactive} from "./reactive";

//! ref 有依赖收集 触发更新

function createRef(rawValue,shallow=false) {
   return new RefImpl(rawValue,shallow);
}

// 判断是否为对象，
const convert = val => isObject(val) ? reactive(val) : val;

// 这个代码 转换成es5  就是defineProperty
class RefImpl {
   constructor(rawValue,shallow) {
      this.rawValue = rawValue;
      this.shallow = shallow;
      // 这里要判断一下 如果是浅的 直接放rawValue  如果是深的呢 要把里面的都变成响应式
      // 怎么把里面的变成响应式：我们用 reactive()  包裹一下
      this._value = shallow ? rawValue : convert(rawValue);
      this.__v_isRef = true; // 申明的实例会被添加__v_isRef属性， 标识是一个ref属性
   }

   // 类的属性访问器
   get value() { // 取值 value 会代理到 _value  return this._value
      // 依赖收集
      track(this,TrackGet,'value');
      return this._value; // 取值 取的就是老值
   }
   set value(newValue) {
      // 判断新值和老值 是否相等，如果有变化
      if (hasChanged(newValue, this.rawValue)) {
         // 触发更新
         trigger(this,TriggerOrSet,'value',newValue,this.rawValue);
         this.rawValue = newValue; // 新值 作为 老值
         // 新值 替换 老值
         // 我们要考虑 是否浅的
         this._value = this.shallow ? newValue : convert(newValue);
      }

   }
}

class ObjectRefImpl {
   constructor(target,key) {
      this.target = target;
      this.key = key;
      this.__v_isRef = true;
   }

   get value() {
      // 取值时
      return this.target[this.key];
   }

   set value(newValue) {
      this.target[this.key] = newValue;
   }
}

//!  ref  有依赖收集 触发更新
export function ref(value) {
   //1。 将普通类型  变成一个对象
   return createRef(value);
}
//! 浅的 ref  有依赖收集 触发更新
export function shallowRef(value) {
   return createRef(value,true);
}

//! toRef 可以把一个对象的值 转成ref类型 ----没有依赖收集
//! 如果代理对象本身是响应式的 那么toRef就是响应式的
export function toRef(target,key) {
   return new ObjectRefImpl(target,key);
}

//! toRefs 可以把多个值转成 ref  ----没有依赖收集
// 我们在使用时  不能直接解构reactive的属性  但是可以用toRefs包装一下 再解构
/*
如： let proxy = reactive({name:'xx',age:10})
      let {name,age} = toRefs(proxy);
      使用：name.value
      修改： proxy.name = 'xxxxx'
*/
export function toRefs(object) { // object 可能是数组  或者对象
   // 是数组的就声明一个一样长度的数组  否则就声明一个空对象
   const ret = isArray(object) ? new Array(object.length) : {};
   for (const key in object) {
      ret[key] = toRef(object,key);
   }
   return ret;
}